import socket
import json
import subprocess
import os
import time
from time import sleep

import platform
import psutil
from win11toast import toast
import logging
import argparse
import requests
from threading import Thread
from datetime import datetime, timedelta
from psutil import cpu_count, virtual_memory, net_io_counters, boot_time


# 参数解析
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=17678, help="监听端口")
parser.add_argument("--host", default="0.0.0.0", help="绑定地址")
parser.add_argument("--log", default="E:/PCshutdown.log", help="日志文件路径")
args = parser.parse_args()

# 日志配置
logging.basicConfig(
    filename=args.log,
    level=logging.INFO,
    encoding='utf-8',
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# 钉钉机器人 Webhook 地址
DINGTALK_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token="

def send_dingtalk(title, text):
    headers = {"Content-Type": "application/json"}
    data = {
        "msgtype": "text",
        "text": {
            "content": f"主机通知：\n{title}\n\n{text}",
            "mentioned_list": []
        }
    }
    try:
        response = requests.post(DINGTALK_WEBHOOK, json=data, headers=headers)
        logging.info("钉钉通知发送成功: %s", response.text)
    except Exception as e:
        logging.error("钉钉通知发送失败: %s", str(e))

def get_ip_address():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("192.168.255.255", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception as e:
        logging.warning("获取IP失败: %s", str(e))
        sleep(10)
        return get_ip_address()

def ptoast(title, text):
    try:
        logging.info(title + " ---- " + text)
        toast(title, text)
    except Exception as e:
        logging.warning("通知失败: %s", str(e))
        pass

def is_connected():
    try:
        socket.create_connection(("www.baidu.com", 80), timeout=5)
        return True
    except OSError:
        return False

def get_uptime():
    """获取系统运行时间"""
    uptime_seconds = time.time() - boot_time()
    return str(timedelta(seconds=int(uptime_seconds)))

# 获取网络 I/O 数据
net_info = net_io_counters()

def check_and_notify_system_status():
    while True:
        cpu_usage = psutil.cpu_percent(interval=1)
        mem = psutil.virtual_memory()
        disk_usage = psutil.disk_usage('/')
        ip = get_ip_address()

        # 获取网络 I/O 数据
        net_info = net_io_counters()

        # 构建 status_text
        status_text = (
            f"⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
            f"🖥️ 主机名: {socket.gethostname()}\n"
            f"💻 IP地址: {ip}\n"
            f"🔌 在线状态: {'在线' if is_connected() else '离线'}\n"
            f"🧠 CPU使用率: {cpu_usage}% | 逻辑核心数: {cpu_count(logical=True)}\n"
            f"💾 内存使用率: {mem.percent}% | 已用: {mem.used // (1024 * 1024)} MB / 总量: {mem.total // (1024 * 1024)} MB\n"
            f"💾 虚拟内存使用率: {virtual_memory().percent}%\n"
            f"📦 系统盘使用率: {disk_usage.percent}% | 剩余空间: {disk_usage.free // (1024 ** 3)} GB\n"
            f"📶 网络上传: {net_info.bytes_sent / (1024 ** 2):.2f} MB | 下载: {net_info.bytes_recv / (1024 ** 2):.2f} MB\n"
            f"⏳ 系统运行时间: {get_uptime()}\n"
        )

        # 添加 CPU 温度（如果支持）
        if platform.system() == "Linux":
            try:
                temps = psutil.sensors_temperatures()
                if 'coretemp' in temps:
                    cpu_temp = temps['coretemp'][0].current
                    status_text += f"🌡️ CPU温度: {cpu_temp}°C\n"
                else:
                    status_text += "🌡️ CPU温度: 不可用\n"
            except Exception as e:
                status_text += f"🌡️ CPU温度: 异常({str(e)})\n"
        else:
            status_text += "🌡️ CPU温度: 仅支持 Linux 平台\n"

        send_dingtalk("【系统状态报告】", status_text)
        time.sleep(3600)  # 每小时一次

UDP_IP = args.host
UDP_PORT = args.port

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((UDP_IP, UDP_PORT))
    msg = f"本机ip：{get_ip_address()}，监听端口：{UDP_PORT}，将保持后台运行"
    logging.info(msg)
    ptoast("PCshutdown已启动", msg)
    send_dingtalk("【PCshutdown已启动】", msg)
except Exception as e:
    logging.critical("启动失败: %s", str(e))
    ptoast("PCshutdown已退出", "启动失败：" + str(e))
    send_dingtalk("【PCshutdown启动失败】", str(e))
    os._exit(1)

# 启动状态监控线程
status_thread = Thread(target=check_and_notify_system_status, daemon=True)
status_thread.start()

while True:
    try:
        data, addr = sock.recvfrom(1024)
        json_data = data.decode()
        received_data = json.loads(json_data)
        if received_data['cmd'] == "shutdown":
            logging.info("收到关机指令: %s", received_data)
            send_dingtalk("【收到关机指令】", f"来自IP: {addr[0]}\n内容: {received_data}")
            ptoast("收到关机指令", f"来自{addr[0]}，将在{received_data['time']}秒后关机")

            subprocess.Popen(['shutdown', '-s', '-t', str(received_data['time'])],
                             stdout=subprocess.DEVNULL,
                             stderr=subprocess.DEVNULL,
                             shell=True)
            sock.sendto('succeeded'.encode('utf-8'), addr)
            send_dingtalk("【关机已触发】", f"将在 {received_data['time']} 秒后关机")
    except json.JSONDecodeError as je:
        logging.error("JSON解析失败: %s, 原始数据: %s", str(je), repr(data))
        sock.sendto('invalid_json'.encode('utf-8'), addr)
        send_dingtalk("【JSON解析错误】", f"原始数据: {repr(data)}")
    except Exception as e:
        logging.error("执行失败: %s", str(e))
        sock.sendto('failed'.encode('utf-8'), addr)
        ptoast("PCshutdown命令执行失败", "error：" + str(e))
        send_dingtalk("【命令执行失败】", str(e))
        continue
